#include <stdio.h>
#include <string.h>
#include "pli.h"
#include "msg.h"


#define SCALER_32 1
#define  DEBUG 0
void mem_to_net(s_tfnodeinfo *info,char * buffer,int count);
void net_to_mem(s_tfnodeinfo *info,char * buffer,int count);
void mem_to_net_be(s_tfnodeinfo *info,char * buffer,char be);
void net_to_mem_be(s_tfnodeinfo *info,char * buffer,char be);
void dump_node_type(PLI_INT16 node_type);
void dump_typep(PLI_INT16 para_type);

enum usertask_id
{
	F_LOADV ,
	F_INTA  ,
	F_MREADBE,
	F_MREAD,
	F_MWRITEBE,
	F_MWRITE,
	F_INTR  
};
/*****************************************************************************
 *
 * pli_load_vector()
 *
 * mem/io wr/rd:$pli_load_vector(addr,buffer,"read"/"write");
 *
 *****************************************************************************/

int pli_load_vector()
{
	int err;
	long address; 
	long count;
	
	s_tfnodeinfo info;
	char buffer[BUFSIZE];
	char *cmd;


    acc_initialize();
	address=acc_fetch_tfarg_int(1);//tf_igetp(1,inst);
	count=32/8; 
	tf_nodeinfo(2,&info);
	cmd=acc_fetch_tfarg_str(3);

	//dump_node_type(info.node_type);

	//if(32<info.node_mem_size)
	//{
	//	io_printf("memory size mismatch.");
	//}

	if(strcmp(cmd,"read")==0)
	{
		PIPE_MSG  msg;	
		msg.hdr.size=SIZE_MREAD;
		msg.u.mread.address_hi=0;
		msg.u.mread.address_lo=address;
		msg.hdr.count=(unsigned char )count;
		msg.hdr.type=MREAD;
		if((err=rpc_call(&msg,(char *)buffer))<0)
		{
			io_printf("rpc_call failed.%d\n",err);
		}

//		mem_to_net(&info,buffer+SIZE_REPLY,count);

		tf_putp(2,*(int *)(buffer+SIZE_REPLY));
		tf_propagatep(2);
	}
	else
	{
		//net_to_mem(&info,(char *)buffer+SIZE_MWRITE,count);
		
		*(int *)(buffer+SIZE_MWRITE)=acc_fetch_tfarg_int(2);
		PIPE_MSG  *msg=(PIPE_MSG*)buffer;	
		msg->hdr.size=SIZE_IOWRITE+count;
		msg->u.mwrite.address_lo=(unsigned short)address;
		msg->hdr.count=(unsigned char )count;
		msg->hdr.type=MWRITE;

		if((err=rpc_call(msg,(char *)buffer))<0)
		{
			io_printf("rpc_call failed.%d\n",err);
		}
	}
#if DEBUG
	io_printf("\npli_load_vector:%x %x(%s)\n",address,count,cmd);
#endif	
    acc_close();

    return 0;
}

/*****************************************************************************
 *
 * pli_readbe()
 *
 * mem rd:$mreadbe(hi,lo,buffer,be);
 *
 *****************************************************************************/
//tf_nodeinfo()
int pli_mreadbe()
{
	int err;
	long address_lo; 
	long address_hi;
	char be;
	
	s_tfnodeinfo info;
	char buffer[BUFSIZE];

    acc_initialize();
	address_lo=acc_fetch_tfarg_int(2);//tf_igetp(1,inst);
	address_hi=acc_fetch_tfarg_int(1); 
	be=(char)acc_fetch_tfarg_int(4); 


	//dump_node_type(info.node_type);
	PIPE_MSG  msg;	
	msg.hdr.size=SIZE_MREAD;
	msg.u.mread.address_hi=address_hi;
	msg.u.mread.address_lo=address_lo;
	msg.hdr.count=8;
	msg.hdr.type=MREAD;
	if((err=rpc_call(&msg,buffer))<0)
	{
		io_printf("rpc_call failed.%d\n",err);
	}

#if !SCALER_32
	tf_nodeinfo(3,&info);
	mem_to_net_be(&info,buffer+SIZE_REPLY,be);
	tf_propagatep(3);
#else
	int old_v=acc_fetch_tfarg_int(3);
	int mask=0;
	if(be &1)mask =0xff;
	if(be &2)mask |=0xff00;
	if(be &4)mask |=0xff0000;
	if(be &8)mask |=0xff000000;
	old_v&=mask;
	int new_v=*(int*)(buffer+SIZE_REPLY);
	new_v &=~mask;
	new_v|=old_v;
	tf_putp(3,new_v);
#endif

#if DEBUG
	io_printf("\npli_mreadbe:%x %x %x\n",address_lo,address_hi,be);
#endif
    acc_close();

    return 0;
}

/*****************************************************************************
 *
 * pli_mwritebe()
 *
 * mem wr:$mwritebe(hi,lo,buffer,be);
 *
 *****************************************************************************/

int pli_mwritebe()
{
	int err;
	long address_lo; 
	long address_hi;
	char be;
	
	s_tfnodeinfo info;
	unsigned char buffer[BUFSIZE];
	int i;

    acc_initialize();
	address_lo=acc_fetch_tfarg_int(2);//tf_igetp(1,inst);
	address_hi=acc_fetch_tfarg_int(1); 
	be=(char)acc_fetch_tfarg_int(4); 

#if !SCALER_32
	tf_nodeinfo(3,&info);

	//dump_node_type(info.node_type);
	if(8>info.node_mem_size*info.node_ngroups)
	{
		io_printf("memory size mismatch.");
	}

	net_to_mem_be(&info,(char *)buffer+SIZE_MWRITE,be);	
#else
	*(int *)(buffer+SIZE_MWRITE)=acc_fetch_tfarg_int(3); 

#endif

#if DEBUG
	io_printf("pli_mwritebe:%x %x %x\n",address_lo,address_hi,be);
	for(i=0;i<4;i++) io_printf("%02x ",buffer[SIZE_MWRITE+i]);
	io_printf("\n");
#endif
	PIPE_MSG  *msg=(PIPE_MSG*)buffer;	
	msg->hdr.size=SIZE_MWRITE+4;
	msg->u.mwrite.address_hi=address_hi;
	msg->u.mwrite.address_lo=address_lo;
	msg->hdr.be=be&0xf;
	msg->hdr.type=MWRITEBE;

	if((err=rpc_call(msg,(char *)buffer))<0)
	{
		io_printf("rpc_call failed.%d\n",err);
	}




    acc_close();

    return 0;
}

/*****************************************************************************
 *
 * pli_read()
 *
 * mem rd:$mread(hi,lo,buffer,count);
 *
 *****************************************************************************/
//tf_nodeinfo()
int pli_mread()
{
	int err;
	long address_lo; 
	long address_hi;
	long count;
	
	s_tfnodeinfo info;
	char buffer[BUFSIZE];

    acc_initialize();
	address_lo=acc_fetch_tfarg_int(2);//tf_igetp(1,inst);
	address_hi=acc_fetch_tfarg_int(1); 

#if !SCALER_32
	count=acc_fetch_tfarg_int(4); 
	tf_nodeinfo(3,&info);
	dump_node_type(info.node_type);
#else 
	count =4;
#endif

	PIPE_MSG  msg;	
	msg.hdr.size=SIZE_MREAD;
	msg.u.mread.address_hi=address_hi;
	msg.u.mread.address_lo=address_lo;
	msg.hdr.count=(unsigned char )count;
	msg.hdr.type=MREAD;
	if((err=rpc_call(&msg,buffer))<0)
	{
		io_printf("rpc_call failed.%d\n",err);
	}
#if !SCALER_32
	mem_to_net(&info,buffer+SIZE_REPLY,count);
	tf_propagatep(3);
#else

	tf_putp(3,*(int*)(buffer+SIZE_REPLY));
#endif

#if DEBUG
	io_printf("\npli_mread:%x %x %x(%x)\n",address_lo,address_hi,count,*(int*)(buffer+SIZE_REPLY));
#endif
    acc_close();

    return 0;
}

/*****************************************************************************
 *
 * pli_mwrite()
 *
 * mem rd:$mwrite(hi,lo,buffer,count);
 *
 *****************************************************************************/

int pli_mwrite()
{
	int err;
	long address_lo; 
	long address_hi;
	long count;
	
	s_tfnodeinfo info;
	unsigned char buffer[BUFSIZE];
	int i;

    acc_initialize();
	address_lo=acc_fetch_tfarg_int(2);//tf_igetp(1,inst);
	address_hi=acc_fetch_tfarg_int(1); 
#if !SCALER_32
	count=acc_fetch_tfarg_int(4); 

	tf_nodeinfo(3,&info);
	//dump_node_type(info.node_type);
	if(count>info.node_mem_size*info.node_ngroups)
	{
		io_printf("memory size mismatch.");
		count=info.node_mem_size*info.node_ngroups;
	}


	net_to_mem(&info,(char *)buffer+SIZE_MWRITE,count);	
#else
	*(int *)(buffer+SIZE_MWRITE)=acc_fetch_tfarg_int(3); 
	count =4;
#endif

#if DEBUG
	io_printf("pli_mwrite:%x %x %x\n",address_lo,address_hi,count);
	for(i=0;i<info.node_mem_size;i++) io_printf("%02x ",buffer[SIZE_MWRITE+i]);
	io_printf("\n");
#endif

	PIPE_MSG  *msg=(PIPE_MSG*)buffer;	
	msg->hdr.size=SIZE_MWRITE+count;
	msg->u.mwrite.address_hi=address_hi;
	msg->u.mwrite.address_lo=address_lo;
	msg->hdr.count=(unsigned char )count;
	msg->hdr.type=MWRITE;

	if((err=rpc_call(msg,(char *)buffer))<0)
	{
		io_printf("rpc_call failed.%d\n",err);
	}




    acc_close();

    return 0;
}

/*****************************************************************************
 *
 * pli_inta()
 *
 * mem rd:$inta();
 *
 *****************************************************************************/
//tf_nodeinfo()
int pli_inta()
{
	int err;
	char buffer[BUFSIZE];
    acc_initialize();
	PIPE_MSG  msg;	
	msg.hdr.size=SIZE_INTA;
	msg.hdr.count=1;
	msg.hdr.type=INTA;
	if((err=rpc_call(&msg,buffer))<0)
	{
		io_printf("rpc_call failed.%d\n",err);
	}
	tf_putp(0,buffer[0]);

	io_printf("\npli_inta:%x \n",buffer[0]);
    acc_close();

    return 0;
}



int checktf(int s)
{
	//io_printf("checktf called.%x\n",s);

	switch(s)
	{
	case F_LOADV:
		if (tf_nump() !=3) { 
			tf_error ("$mread()\n"); 
		}

		if (tf_typep(2) != tf_readwrite) { 
			tf_error("$load_vector:"); 
			tf_error ("The argument must be a register type\n"); 
		}
		
		/*for(int i=1;i<4;i++) 
		{
			io_printf("\n%d:\n",i);
			dump_typep(tf_typep(i));
		}*/
		break;

	case F_INTA:
		if (tf_nump() !=0) { 
			tf_error ("$inta()\n"); 
		}
		break;

	case F_INTR:
		if (tf_nump() !=3) { 
			tf_error ("$intrGenInit()\n"); 
		}
		break;
	case F_MREAD:
	case F_MWRITE:
	case F_MREADBE:
	case F_MWRITEBE:
		if (tf_nump() !=4) { 
			tf_error ("$mwrite()/$mread/$mwritebe()/$mreadbe\n"); 
		}
		break;
	}

	return 0;
} 
int sizetf(int s)
{
	PLI_INT32 arg_cnt = tf_nump();
	io_printf("sizetf called.%x\n",s);
	
	switch(s)
	{
	case F_LOADV:
		if (tf_sizep(2)  !=32) { 
			tf_error ("$load_vector()\n"); 
		}
	
		break;
	}
	

	return 0;
} 



int misctf(int s)
{
	//io_printf("misctf called.%x\n",s);

	switch(s)
	{
	case F_INTR:
		break;
	}
	

	return 0;
} 


void intrReactive (int data, int reason, int paramvc);
void intrGenInit();



/*****************************************************************************
 *
 * Required structure for initializing PLI routines.
 *
 *****************************************************************************/
extern "C"
{
__declspec(dllexport)  s_tfcell veriusertfs[] =
{
    { usertask, F_MREAD, (p_tffn)checktf, (p_tffn)sizetf, pli_mread,  (p_tffn)misctf,  "$mread"  },
    { usertask, F_MWRITE, (p_tffn)checktf, (p_tffn)sizetf, pli_mwrite, (p_tffn)misctf,  "$mwrite" },
    { usertask, F_MREADBE, (p_tffn)checktf, (p_tffn)sizetf, pli_mreadbe,  (p_tffn)misctf,  "$mreadbe"  },
    { usertask, F_MWRITEBE, (p_tffn)checktf, (p_tffn)sizetf, pli_mwritebe, (p_tffn)misctf,  "$mwritebe" },
    { usertask,     F_LOADV,(p_tffn)checktf, (p_tffn)sizetf, pli_load_vector,(p_tffn)misctf,  "$load_vector"  },
    { userfunction, F_INTA, (p_tffn)checktf, (p_tffn)sizetf, pli_inta,   (p_tffn)misctf,  "$inta"   },
    { usertask,     F_INTR, (p_tffn)0,       (p_tffn)0,   (p_tffn)intrGenInit,(p_tffn)intrReactive,  "$intrGenInit"},

	{0} /*** final entry must be 0 ***/
};


__declspec(dllexport)  char *veriuser_version_str =
"\n =============================\n"
"VERILOG PLI inteface for hardware C model \n" 
"\n=============================\n"; 
__declspec(dllexport) int (*endofcompile_routines[])() ={0}; 

}

/*****************************************************************************/





